package owg.util.data;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;

import javax.imageio.ImageIO;
import com.jogamp.opengl.GL;
import com.jogamp.opengl.GL2;
import com.jogamp.opengl.GL3;
import com.jogamp.opengl.GLContext;

import com.jogamp.common.nio.Buffers;
import com.jogamp.opengl.util.GLBuffers;

/**
 * An interleaved true color CPU-side image. 
 * */
public class IImage
	{
	public enum Type
		{
		RGBA(4), 
		RGB(3), 
		GRAY(1);
		
		public final int bytesPerComponent;
		
		private Type(int bytes)
			{
			this.bytesPerComponent = bytes;
			}
		}
	/**Advice to self: Name your objects.*/
	public final String name;
	public final byte[] data;
	public final int width, height;
	public final Type type;

	public IImage(String string) throws IOException
		{
		this(new File(string));
		}
	
	public IImage(File f) throws IOException
		{
		this(ImageIO.read(f), f.getName());
		}
	
	public IImage(BufferedImage bi, String name)
		{
		this.name = name;
		width = bi.getWidth();
		height = bi.getHeight();
		if(bi.getType() == BufferedImage.TYPE_4BYTE_ABGR)
			type = Type.RGBA;
		else if(bi.getType() == BufferedImage.TYPE_3BYTE_BGR)
			type = Type.RGB;
		else if(bi.getType() == BufferedImage.TYPE_BYTE_GRAY)
			type = Type.GRAY;
		else //Render into a supported type.
			{
			BufferedImage tmp = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
			Graphics g = tmp.getGraphics();
			g.drawImage(bi, 0, 0, null);
			g.dispose();
			bi = tmp;
			type = Type.RGBA;
			}

		data = new byte[width*height*type.bytesPerComponent];
		bi.getRaster().getDataElements(0, 0, width, height, data);
		}
	
	public IImage(Type type, byte[] data, int width, int height, String name)
		{
		super();
		this.name = name;
		this.data = data;
		this.width = width;
		this.height = height;
		this.type = type;
		}
	
	public IImage(Type type, int width, int height, String name)
		{
		super();
		this.name = name;
		this.data = new byte[width*height*type.bytesPerComponent];
		this.width = width;
		this.height = height;
		this.type = type;
		}

	public<T extends GL> int createTexture(boolean forcePOTS, boolean bilinearMin, boolean bilinearMag, boolean hWrap, boolean vWrap)
		{
		int[] texArray = new int[1];
		GL gl = GLContext.getCurrentGL();
		gl.glGenTextures(1, texArray, 0);
		int texture = texArray[0];
		final int glType;
		if(type == Type.GRAY)
			{
			//PITA: RED for PP, LUMINANCE for FFP
			if(gl.isGL3())
				glType = GL3.GL_RED;
			else if(gl.isGL2())
				glType = GL2.GL_LUMINANCE;
			else 
				throw new RuntimeException("GL not supported: "+gl);
			}
		else if(type == Type.RGB)
			glType = GL.GL_RGB;
		else
			glType = GL.GL_RGBA;
		
		gl.glBindTexture(GL.GL_TEXTURE_2D, texture);
		
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, hWrap?GL.GL_REPEAT:GL.GL_CLAMP_TO_EDGE);
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, vWrap?GL.GL_REPEAT:GL.GL_CLAMP_TO_EDGE);

		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, bilinearMin?GL.GL_LINEAR:GL.GL_NEAREST);
		gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, bilinearMag?GL.GL_LINEAR:GL.GL_NEAREST);
		
		if(forcePOTS)
			{
			int textureWidth = GLBuffers.getNextPowerOf2(width);
        	int textureHeight = GLBuffers.getNextPowerOf2(height);
        	System.out.print("->"+textureWidth+"x"+textureHeight+" ");
        	//Allocate empty data
        	gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA8, textureWidth, textureHeight, 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, Buffers.newDirectByteBuffer(textureWidth*textureHeight*4));
        	//Transfer to relevant part of texture
        	gl.glTexSubImage2D(GL.GL_TEXTURE_2D, 0, 0, 0, width, height, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap(data));
			}
		else
			{
			gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, glType, width, height, 0, glType, GL.GL_UNSIGNED_BYTE, ByteBuffer.wrap(data));
			}
		
		return texture;
		}
	}
